/**
 * Adapted from Sapa
 */
package gov.nasa.anml.lifted;

import java.util.ArrayList;

import gov.nasa.anml.PDDL;
import gov.nasa.anml.State;
import gov.nasa.anml.PDDL.BooleanExpression;
import gov.nasa.anml.PDDL.Time;
import gov.nasa.anml.utility.*;


// the better solution is to introduce explicit Cast expressions

// also, this should be an operation class that does every operation
// and condition should be the conversion of expressions to conditions, i.e., 
// conditions == expression statements (<expression> ';')

// In line with that, to convert float expressions into conditions (e.g.),
// in pddl, one can write (== (* <float-expression> 0) 0)
// as that ensures that all the referenced functions are defined, and perhaps
// also that the operations don't result in NaN.  Or if multiplication is verboten
// (?why?) then one can do (== (- <e> <e>) 0) where <e> == <float-expression>
public class OpUnary<I,O extends SimpleObject<? super O>> extends ExpressionImp<O,O> {
	public Op op;
	public Expression<I,?> exp;
	public TypeCode baseType;

	public OpUnary() {
		op = Op.exists;
		exp = null;
		baseType = TypeCode.Boolean;
	}

	public OpUnary(TypeCode baseType, Op op, Expression<I,?> exp) {
		this.exp = exp;
		this.op = op;
		this.baseType = baseType;
	}


	// should implement following in expression
	// and then do leftSide.toInfix() etc.
	public String toString() {
		StringBuilder s = new StringBuilder();
		s.append('(').append(op).
		  append(' ').append(exp).
		  append(')');
		return s.toString();
	}

	public O value(State s) {
		I e = exp.value(s);
		if (e == null)
			return null;
		switch(exp.typeCode()) {
		case Boolean:
			return (O) op.eval((SimpleBoolean)e);
		case Byte:
			break;
		case Short:
			break;
		case Integer:
			return (O) op.eval((SimpleInteger)e);
		case Long:
			break;
		case Float:
			return (O) op.eval((SimpleFloat)e);
		case Double:
			break;
		case String:
			return (O) op.eval((SimpleString)e);
		case Symbol:
			break;
		case Vector:
			break;
		case Object:
			break;
		}
		return null;
	}

	public TypeCode typeCode() {
		return baseType;
	}
	
	public History<O> storage(State p, State c) {
		return null;
	}
	
	public boolean apply(State p, int contextID, State c) {
		if (baseType != TypeCode.Boolean)
			return false;
		I e = exp.value(p);
		if (e == null)
			return false;
		SimpleBoolean ret = null;
		switch(exp.typeCode()) {
		case Boolean:
			ret = (SimpleBoolean) op.eval((SimpleBoolean)e);
		case Byte:
			break;
		case Short:
			break;
		case Integer:
			ret = (SimpleBoolean) op.eval((SimpleInteger)e);
		case Long:
			break;
		case Float:
			ret = (SimpleBoolean) op.eval((SimpleFloat)e);
		case Double:
			break;
		case String:
			ret = (SimpleBoolean) op.eval((SimpleString)e);
		case Symbol:
			break;
		case Vector:
			break;
		case Object:
			break;
		}
		if (ret != ANMLBoolean.True)
			return false;
		return true;
	}
	
	public transient PDDL.Expression asPDDLExpression;
	
	public void translateDecl(PDDL pddl, Interval unit) {
		exp.translateDecl(pddl,unit);
	}
	
	public PDDL.Argument translateArgument(PDDL pddl, Interval unit) {
		switch(op) {
		case ref:
			return exp.translateArgument(pddl,unit);
		case refNot:
		case not:
		default:
			return super.translateArgument(pddl,unit);
		}
	}

	public PDDL.Expression translateLValue(PDDL pddl,Interval unit) {
		switch(op) {
		case ref:
			return exp.translateLValue(pddl,unit);
		case refNot:
		case not:
		default:
			return super.translateLValue(pddl,unit);
		}
	}
	

	public PDDL.Expression translateExpr(PDDL pddl, Interval unit) {
		if (asPDDLExpression != null)
			return asPDDLExpression;
		if(op == Op.ref)
			return asPDDLExpression = exp.translateExpr(pddl,unit);
		switch(baseType) {
		case Boolean:
			switch(op) {
			case refNot:
			case not:
				return asPDDLExpression = pddl.negate((BooleanExpression)exp.translateExpr(pddl,unit));
			default:
				return asPDDLExpression = pddl.wrap(op.pddl,(PDDL.BooleanExpression) exp.translateExpr(pddl,unit));
			}
		case Float:
				return asPDDLExpression = pddl.wrap(op.pddl,(PDDL.FloatExpression) exp.translateExpr(pddl,unit));
		}
		System.err.println("Uh-oh, don't know how to translate: "+this);
		return asPDDLExpression = pddl.wrap(op.pddl,translateExpr(pddl,unit));
	}


}
